package com.vislardo.app.notebook.utils;

import com.vislardo.app.notebook.error.FileEncryptorException;
import com.vislardo.app.notebook.model.NoteBook;
import com.vislardo.app.notebook.model.NoteInfo;
import com.vislardo.app.notebook.model.NotePage;


import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.*;

import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class NoteFileManager {
    private static volatile NoteFileManager instance;

    private String filepath;

    private String tempFilePath;

    private static final String TEMP_PREFIX = "tmp_";

    private static final String FILE_SUFFIX = ".ven";

    private static final String TEMP_FILE_SUFFIX = ".vdn";

    private static final String PAGE_SPR = "<![CDATA[PAGE_END]]>";


    private NoteFileManager(){}

    public static NoteFileManager getInstance(){
        if(instance==null){
            synchronized (NoteFileManager.class){
                if(instance==null){
                    instance = new NoteFileManager();
                    AppConfigManager configer = AppConfigManager.getInstance();
                    instance.filepath = configer.getNoteFolderPath();
                    instance.tempFilePath = configer.getTempFolderPath();
                }
            }
        }
        return instance;
    }



    private NoteBook loadBook(String filename){
        List<NotePage> pages = new ArrayList<>();
        try(Stream<String> lines = Files.lines(Path.of(this.tempFilePath,TEMP_PREFIX + filename+TEMP_FILE_SUFFIX))){
            //定义累加器来收集分段内容
            List<String> segments = new ArrayList<>();
            StringBuilder currentSegment = new StringBuilder();

            lines.forEach(line -> {
                if(PAGE_SPR.equals(line.trim())){
                    if(currentSegment.length() >0){
                        segments.add(currentSegment.toString());
                        currentSegment.setLength(0);
                    }
                }else{
                    currentSegment.append(line).append("\n");
                }
            });

            if(currentSegment.length()>0){
                segments.add(currentSegment.toString());
            }

            pages = segments.stream()
                    .map(segment -> new NotePage(segments.indexOf(segment)+1,segment.trim()))
                    .collect(Collectors.toList());

        } catch (IOException e){
            e.printStackTrace();
            return NoteBook.ivalidNote();
        }
        return new NoteBook(pages, filename);
    }

    private boolean appendPage(String fileName,NotePage page){
        try(OutputStream out = Files.newOutputStream(
                                    Path.of(tempFilePath,TEMP_PREFIX+fileName+TEMP_FILE_SUFFIX),
                                    StandardOpenOption.CREATE,
                                    StandardOpenOption.WRITE,
                                    StandardOpenOption.APPEND
                                )
            ){
                out.write((page.getContent()+"\n"+PAGE_SPR+"\n").getBytes());
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        return true;
    }
    private SecretKey getKey(String keyStr){
        byte[] keyBytes = Base64.getDecoder().decode(keyStr);

        // 恢复 SecretKey
        return new SecretKeySpec(keyBytes, "AES");
    }

    /**
     *
     * @param info
     * @return
     */
    public NoteBook loadEncrtyptBook(NoteInfo info){
        String fileName = info.getNoteName();
        SecretKey key = getKey(info.getNoteKey());
        Path notepath = Path.of(filepath, fileName+FILE_SUFFIX);
        if(Files.notExists(notepath)){
            return NoteBook.startNewNote(fileName);
        }

       try {
           //文件解密到临时路径
           FileEncryptor.decryptFile(key,
                   Path.of(filepath, fileName+FILE_SUFFIX),
                   Path.of(tempFilePath, TEMP_PREFIX + fileName+TEMP_FILE_SUFFIX));
//           加载笔记内容
           NoteBook note = loadBook(fileName);
           return note;
       } catch (FileEncryptorException e) {
           System.err.println("文件解密时发生错误: " + e.getMessage());
       } catch (IOException e) {
           System.err.println("读写文件时发生IO错误: " + e.getMessage());
       } finally {
//            删除临时文件
           try{
               Files.deleteIfExists(Path.of(tempFilePath,TEMP_PREFIX + fileName+TEMP_FILE_SUFFIX));
           } catch (IOException e) {
               System.err.println("删除文件时发生错误: " + e.getMessage());
           }
       }
       return null;
    }

    /**
     *
     * @param info
     * @param page
     * @return
     */
    public NoteInfo appendPageToEncrtyptBook(NoteInfo info,NotePage page){
        String fileName = info.getNoteName();
        SecretKey key = getKey(info.getNoteKey());

        Path encPATH = Path.of(this.filepath, fileName+FILE_SUFFIX);
        Path decPath = Path.of(this.tempFilePath, TEMP_PREFIX + fileName+TEMP_FILE_SUFFIX);
        try {
            FileEncryptor.decryptFile(key,encPATH,decPath);
            //写入新的页
            if(!this.appendPage(fileName,page)){
                return info;
            }
            Path encPath_tmp = Path.of(tempFilePath, fileName+FILE_SUFFIX);
            //重新加密文件
            SecretKey newKey = FileEncryptor.encryptFile(decPath,encPath_tmp);
            // 如果新加密文件存在（判断新文件存在并完整？）
            if(Files.exists(encPath_tmp)){
                //保存新的密钥
                String encodeKey = Base64.getEncoder().encodeToString(newKey.getEncoded());
                info.setNewKey(encodeKey);

                DataManager.getInstance().saveNewBookInfo(info);
                //替换旧的加密文件。
                Files.move(encPath_tmp,encPATH,StandardCopyOption.REPLACE_EXISTING);
            }
            return info;
        } catch (FileEncryptorException e) {
            System.err.println("文件解密时发生错误: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("读写文件时发生IO错误: " + e.getMessage());
        } finally {
//           删除临时文件
            try{
                Files.deleteIfExists(decPath);
            } catch (IOException e) {
                System.err.println("删除文件时发生错误: " + e.getMessage());
            }
        }
        return null;
    }


    public NoteInfo createNewNote(String code){
//        创建新的日记记录
        NoteInfo note = new NoteInfo();
        note.setNoteCode(code);
        SnowFlakeHandler handler = new SnowFlakeHandler(0,0);
        note.setNoteName(StrLableUtil.nsChange_32(handler.nextId()));

        //创建文件的路径
        Path decPath = Path.of(this.tempFilePath, TEMP_PREFIX + note.getNoteName()+TEMP_FILE_SUFFIX);
        //加密后的临时文件路径
        Path encPath_tmp = Path.of(tempFilePath, note.getNoteName()+FILE_SUFFIX);
        try {
            //写入第一页
            if (!appendPage(note.getNoteName(), NotePage.getNewPage(null))) {
                return null;
            }

            SecretKey encKey = FileEncryptor.encryptFile(decPath, encPath_tmp);

            Path encPATH = Path.of(this.filepath, note.getNoteName()+FILE_SUFFIX);

            Files.move(encPath_tmp, encPATH, StandardCopyOption.REPLACE_EXISTING);

            String encodeKey = Base64.getEncoder().encodeToString(encKey.getEncoded());

            note.setNewKey(encodeKey);

            return note;

        } catch (FileEncryptorException e) {
            System.err.println("文件加密时发生错误: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("读写文件时发生IO错误: " + e.getMessage());
        } finally {
            //           删除临时文件
            try{
                Files.deleteIfExists(decPath);
            } catch (IOException e) {
                System.err.println("删除文件时发生错误: " + e.getMessage());
            }
        }
        return null;
    }
}
